#include "Shape.h"
#include "LightAbstractRenderer.h"
#include "Triangulate.h"
#include "BasicType.h"
#include "CharacterList.h"


namespace LightPlayer{

	static bool s_ApproximateBezierCurve = true;
	static int s_ItterationOfBezierCurveApproximation = 5;

	  /* ************* */
	 /*		Shape     */
	/* ************* */

	Shape::Shape(){
		init();
	}

	void Shape::init(){
		m_CharacterId = 0;
		m_ShapeBounds.init();
		m_SwfShape.init();
	}

	void Shape::readFromStream( Stream* stream, int tag_shape /*= eDefineShape*/, CharacterList* pCharacterList /*= NULL*/, bool is_font_glyph /*= false*/  ){
		
		// Step 1 : read Swf direct format
		m_SwfShape.readFromStream(stream,tag_shape, is_font_glyph);

		m_ShapeBounds = m_SwfShape.m_ShapeBounds;

		m_FillStyles = m_SwfShape.m_ShapeWithStyle.m_FillStyles;
		m_LineStyles = m_SwfShape.m_ShapeWithStyle.m_LineStyles;

		if( is_font_glyph ){
			m_FillStyles.m_FillStyles.push_back( FILLSTYLEARRAY::s_white_fill_style );
		}

		// resolved bitmap character dependencies
		if(pCharacterList){
			for (int i=0; i<m_FillStyles.size(); ++i ){
				FILLSTYLE& fill = m_FillStyles.at(i+1);
				if( fill.isBitmapFill() ){
					Bitmap* bitmap = pCharacterList->getBitmap( fill.getBitmapId() );
					fill.setBitmap( bitmap );
				}
			}
		}


		// Step 2 : Convert Shape Record To Path Class

		vec2 drawPos;
		uint32 line_style = 0;
		uint32 fill_style_0 = 0;
		uint32 fill_style_1 = 0;

		SHAPE_WITHSTYLE& m_ShapeWithStyle = m_SwfShape.m_ShapeWithStyle;
		
		int number_record = m_ShapeWithStyle.m_ShapeRecords.size();
		for (int i=0; i<number_record; i++){
			SHAPERECORD* record = &( m_ShapeWithStyle.m_ShapeRecords[i] );
			
			if( record->m_TypeRecord == eStyleChangeRecord ){
				if( record->m_StateFillStyle1 ){
					fill_style_1 = record->m_FillStyle1;
				}
				if( record->m_StateFillStyle0 ){
					if(!is_font_glyph){
						fill_style_0 = record->m_FillStyle0;
					}
					else{
						fill_style_1 = record->m_FillStyle0; // in Font glyph, path use fillStyle0 but they draw the right part of the vector, 
						// so FillStyle1 should be use instead, to get correct value in isClockwise boolean and the triangulation then.
					}
				}
				if( record->m_StateLineStyle ){
					line_style = record->m_LineStyle;
				}
				
				if( record->m_StateMoveTo ){
					drawPos.set( record->m_MoveDeltaX, record->m_MoveDeltaY );
				}
			}
			else if( record->m_TypeRecord == eStraightEdgeRecord ){

				vec2 nextPos = drawPos;
				if(  record->m_general_line_flag || ! record->m_vertical_line_flag){
					nextPos.x += record->m_MoveDeltaX;
				}
				if(  record->m_general_line_flag ||  record->m_vertical_line_flag){
					nextPos.y += record->m_MoveDeltaY;
				}

				// add line here 
				if(fill_style_0 != 0 ){
					AddStraightEdgeToFilledPath( drawPos, nextPos, fill_style_0, false );
				}

				if(fill_style_1 != 0 ){
					AddStraightEdgeToFilledPath( drawPos, nextPos, fill_style_1, true );
				}

				if(line_style != 0 ){
					AddStraightEdgeToLinePath( drawPos, nextPos, line_style );
				}
				// end

				drawPos = nextPos;
			}
			else if( record->m_TypeRecord == eCurvedEdgeRecord ){

				vec2 offCurveControl( drawPos.x + record->m_ControlDeltaX, drawPos.y + record->m_ControlDeltaY );
				vec2 nextPos( offCurveControl.x + record->m_AnchorDeltaX, offCurveControl.y + record->m_AnchorDeltaY );

				// add line here 
				if(fill_style_0 != 0 ){
					AddCurvedEdgeToFilledPath( drawPos, offCurveControl, nextPos, fill_style_0, false );
				}

				if(fill_style_1 != 0 ){
					AddCurvedEdgeToFilledPath( drawPos, offCurveControl, nextPos, fill_style_1, true );
				}

				if(line_style != 0 ){
					AddCurvedEdgeToLinePath( drawPos, offCurveControl, nextPos, line_style );
				}
				// end

				drawPos = nextPos;
			}
		}


		// Step 3 : compute Is Clockwise and so if is an hole
		uint32 filled_path_number = m_Paths.size();
		for (uint32 i=0; i<filled_path_number; i++){
			m_Paths[i].computeIsClockwise();
		}

		// Step 4 : resolve hole
		for (uint32 i=0; i<filled_path_number; i++){
			if( m_Paths[i].isHole() ){
				for (uint32 j=0; j<filled_path_number; j++){
					if( ! m_Paths[j].isHole() && m_Paths[j].testIsHoleInside( & m_Paths[i] )  ){
						m_Paths[j].addHole( & m_Paths[i]  );
						break;
					}
				}
			}
		}

		// Step 5 : Triangulate filled path
		for (uint32 i=0; i<filled_path_number; i++){
			if( ! m_Paths[i].isHole() ){
				m_Paths[i].triangulate();
			}
		}
	}

	void Shape::AddStraightEdgeToFilledPath( const vec2& StartPos,const vec2& EndPos, uint32 fill_index, bool is_right_side_filled /*= false*/ ){
		bool place_finded = false;

		vec2 StartToEnd( EndPos.x - StartPos.x, EndPos.y - StartPos.y );
		vec2 EndToStart( StartPos.x - EndPos.x, StartPos.y - EndPos.y );

		for ( uint32 i=0; i<m_Paths.size() && place_finded == false; i++ ){
			Path& path = m_Paths[i];
			// check if style are compatible
			if( path.getFillIndex() != fill_index ){
				continue;
			}
			// compute Start and End of path to test
			vec2 StartPath = path.getStartPos();
			vec2 EndPath = path.getEndPos();
			// if the Start or the End of the path are link with the Start or the End of the Edge,
			// add this edge in this path and exit (invert the direction of the edge if needed).
			if( EndPath.equalByEpsilon(StartPos) ){
				path.pushBackEdge( Edge(StartToEnd) );
				place_finded = true;
			}
			else if( EndPath.equalByEpsilon(EndPos) ){
				path.pushBackEdge( Edge(EndToStart) );
				place_finded = true;
			}
			else if( StartPath.equalByEpsilon(StartPos) ){
				path.pushFrontEdge( Edge(EndToStart) );
				place_finded = true;
			}
			else if( StartPath.equalByEpsilon(EndPos) ){
				path.pushFrontEdge( Edge(StartToEnd) );
				place_finded = true;
			}
		}

		// if no path where to add our edge, create a new one
		if( place_finded == false ){
			Path newPath;
			newPath.setFillStyleArray( &m_FillStyles );
			newPath.setFillIndex( fill_index );
			newPath.setStartPos( StartPos );
			newPath.setFillRightSide(is_right_side_filled);
			newPath.pushBackEdge( Edge(StartToEnd) );
			m_Paths.push_back( newPath );
		}
	}

	void Shape::AddCurvedEdgeToFilledPath( const vec2& StartPos, const vec2& OffLineControl, const vec2& EndPos, uint32 fill_index, bool is_right_side_filled /*= false*/ ){
		bool place_finded = false;

		vec2 StartToOff( OffLineControl.x - StartPos.x, OffLineControl.y - StartPos.y );
		vec2 OffToEnd( EndPos.x - OffLineControl.x, EndPos.y - OffLineControl.y );

		vec2 EndToOff( OffLineControl.x - EndPos.x, OffLineControl.y - EndPos.y );
		vec2 OffToStart( StartPos.x - OffLineControl.x, StartPos.y - OffLineControl.y );


		for ( uint32 i=0; i<m_Paths.size() && place_finded == false; i++ ){
			Path& path = m_Paths[i];
			// check if style are compatible
			if( path.getFillIndex() != fill_index ){
				continue;
			}
			// compute Start and End of path to test
			vec2 StartPath = path.getStartPos();
			vec2 EndPath = path.getEndPos();
			// if the Start or the End of the path are link with the Start or the End of the Edge,
			// add this edge in this path and exit (invert the direction of the edge if needed).
			if( EndPath.equalByEpsilon(StartPos) ){
				path.pushBackEdge( Edge(StartToOff, OffToEnd) );
				place_finded = true;
			}
			else if( EndPath.equalByEpsilon(EndPos) ){
				path.pushBackEdge( Edge(EndToOff, OffToStart) );
				place_finded = true;
			}
			else if( StartPath.equalByEpsilon(StartPos) ){
				path.pushFrontEdge( Edge(EndToOff, OffToStart) );
				place_finded = true;
			}
			else if( StartPath.equalByEpsilon(EndPos) ){
				path.pushFrontEdge( Edge(StartToOff, OffToEnd) );
				place_finded = true;
			}
		}

		// if no path where to add our edge, create a new one
		if( place_finded == false ){
			Path newPath;
			newPath.setFillStyleArray( &m_FillStyles );
			newPath.setFillIndex( fill_index );
			newPath.setStartPos( StartPos );
			newPath.setFillRightSide(is_right_side_filled);
			newPath.pushBackEdge( Edge(StartToOff, OffToEnd) );
			m_Paths.push_back( newPath );
		}
	}

	void Shape::AddStraightEdgeToLinePath( const vec2& StartPos,const vec2& EndPos, uint32 fill_index ){
		bool place_finded = false;

		vec2 StartToEnd( EndPos.x - StartPos.x, EndPos.y - StartPos.y );
		vec2 EndToStart( StartPos.x - EndPos.x, StartPos.y - EndPos.y );

		for ( uint32 i=0; i<m_Lines.size() && place_finded == false; i++ ){
			Path& path = m_Lines[i];
			// check if style are compatible
			if( path.getFillIndex() != fill_index ){
				continue;
			}
			// compute Start and End of path to test
			vec2 StartPath = path.getStartPos();
			vec2 EndPath = path.getEndPos();
			// if the Start or the End of the path are link with the Start or the End of the Edge,
			// add this edge in this path and exit (invert the direction of the edge if needed).
			if( EndPath.equalByEpsilon(StartPos) ){
				path.pushBackEdge( Edge(StartToEnd) );
				place_finded = true;
			}
			else if( EndPath.equalByEpsilon(EndPos) ){
				path.pushBackEdge( Edge(EndToStart) );
				place_finded = true;
			}
			else if( StartPath.equalByEpsilon(StartPos) ){
				path.pushFrontEdge( Edge(EndToStart) );
				place_finded = true;
			}
			else if( StartPath.equalByEpsilon(EndPos) ){
				path.pushFrontEdge( Edge(StartToEnd) );
				place_finded = true;
			}
		}

		// if no path where to add our edge, create a new one
		if( place_finded == false ){
			Path newPath;
			newPath.setFillIndex( fill_index );
			newPath.setStartPos( StartPos );
			newPath.pushBackEdge( Edge(StartToEnd) );
			m_Lines.push_back( newPath );
		}

	}

	void Shape::AddCurvedEdgeToLinePath( const vec2& StartPos, const vec2& OffLineControl, const vec2& EndPos, uint32 fill_index ){
		bool place_finded = false;

		vec2 StartToOff( OffLineControl.x - StartPos.x, OffLineControl.y - StartPos.y );
		vec2 OffToEnd( EndPos.x - OffLineControl.x, EndPos.y - OffLineControl.y );

		vec2 EndToOff( OffLineControl.x - EndPos.x, OffLineControl.y - EndPos.y );
		vec2 OffToStart( StartPos.x - OffLineControl.x, StartPos.y - OffLineControl.y );


		for ( uint32 i=0; i<m_Lines.size() && place_finded == false; i++ ){
			Path& path = m_Lines[i];
			// check if style are compatible
			if( path.getFillIndex() != fill_index ){
				continue;
			}
			// compute Start and End of path to test
			vec2 StartPath = path.getStartPos();
			vec2 EndPath = path.getEndPos();
			// if the Start or the End of the path are link with the Start or the End of the Edge,
			// add this edge in this path and exit (invert the direction of the edge if needed).
			if( EndPath.equalByEpsilon(StartPos) ){
				path.pushBackEdge( Edge(StartToOff, OffToEnd) );
				place_finded = true;
			}
			else if( EndPath.equalByEpsilon(EndPos) ){
				path.pushBackEdge( Edge(EndToOff, OffToStart) );
				place_finded = true;
			}
			else if( StartPath.equalByEpsilon(StartPos) ){
				path.pushFrontEdge( Edge(EndToOff, OffToStart) );
				place_finded = true;
			}
			else if( StartPath.equalByEpsilon(EndPos) ){
				path.pushFrontEdge( Edge(StartToOff, OffToEnd) );
				place_finded = true;
			}
		}

		// if no path where to add our edge, create a new one
		if( place_finded == false ){
			Path newPath;
			newPath.setFillIndex( fill_index );
			newPath.setStartPos( StartPos );
			newPath.pushBackEdge( Edge(StartToOff, OffToEnd) );
			m_Lines.push_back( newPath );
		}
	}

	void Shape::draw( LightAbstractRenderer* pRenderer, SceneObject* pInstance ){

		static bool draw_tris = true;
		static bool draw_path = false;

		if(draw_tris){
			drawFilledPathTriangulate(  pRenderer, pInstance );
		}
		if(draw_path){
			drawFilledPath( pRenderer, pInstance );
		}
		drawLinePath( pRenderer, pInstance );
	}

	void Shape::drawFilledPath( LightAbstractRenderer* pRenderer, SceneObject* pInstance ){
		vec2 drawPos;
		vec2 controlPos;
		vec2 nextPos;

		uint32 number_path = m_Paths.size();
		for (uint32 i=0; i<number_path; i++){
			if( m_Paths[i].isHole() ){
				continue;
			}

			const array<Edge>& _egdes = m_Paths[i].getEdges();
			uint32 number_edges = _egdes.size();

			drawPos = m_Paths[i].getStartPos();
			pRenderer->setCurrentColor(1,1,1,1);
			pRenderer->setPointSize(1);
			pRenderer->DrawPoint( drawPos );
			pRenderer->setPointSize(1);

			pRenderer->setCurrentColor(1,0,1,1);

			for (uint32 j=0; j<number_edges; j++){
				const Edge& _edge = _egdes[j];

				if( j>0 ){
					pRenderer->setCurrentColor(1,1,1,1);
					pRenderer->setPointSize(1);
					pRenderer->DrawPoint( drawPos );
					pRenderer->setPointSize(1);
					pRenderer->setCurrentColor(1,0,1,1);
				}

				if( _edge.getType() == Edge::eStraightEdge ){
					nextPos = drawPos + _edge.getDelta();
					pRenderer->DrawLine( drawPos, nextPos );
					drawPos = nextPos;
				}
				else {
					controlPos = drawPos + _edge.getOffLineControl();
					nextPos = controlPos + _edge.getDelta();
					pRenderer->DrawQuadBezier( drawPos, controlPos, nextPos, 10 );
					drawPos = nextPos;
				}
			}
		}
	}

	void Shape::drawFilledPathTriangulate( LightAbstractRenderer* pRenderer, SceneObject* pInstance ){

		uint32 number_path = m_Paths.size();
		for (uint32 i=0; i<number_path; i++)
		{
			if( m_Paths[i].isHole() ){
				continue;
			}

			if( m_Paths[i].isTriangulationSucceed() )
			{
				FILLSTYLE& style = m_FillStyles.at( m_Paths[i].getFillIndex() );

				if( style.isBitmapFill() ){ // bitmap
					const array<vec2>& tris = m_Paths[i].getTris();
					const array<vec2>& uvs = m_Paths[i].getUVs();
					uint32 texture = style.getBitmap()->getRendererTextureKey();

					pRenderer->DrawSolidTrisTextured( tris, uvs,  texture );
				}
				else if( style.isGradientFill() ){ // gradient
					const array<vec2>& tris = m_Paths[i].getTris();
					const array<vec2>& uvs = m_Paths[i].getUVs();

					pRenderer->DrawSolidTrisGradient( tris, uvs,  style.getGradient() );
				}
				else // just color
				{
					pRenderer->setCurrentColor( style.m_Color.R01(), style.m_Color.G01(), style.m_Color.B01(), style.m_Color.A01() );
					const array<vec2>& tris = m_Paths[i].getTris();
					pRenderer->DrawSolidTris( tris );

					static bool drawCurveAsTris = true;
					if(drawCurveAsTris && !s_ApproximateBezierCurve){
						pRenderer->setCurrentColor( 0.0f, 1.0f, 0.0f, 1.0f );
						const array<vec2>& internalCurveTris = m_Paths[i].getInternalCurveTris();
						pRenderer->DrawSolidTris( internalCurveTris );

						pRenderer->setCurrentColor( 1.0f, 1.0f, 0.0f, 1.0f );
						const array<vec2>& externalCurveTris = m_Paths[i].getExternalCurveTris();
						pRenderer->DrawSolidTris( externalCurveTris );
					}
				}
			}
		}
	}

	void Shape::drawLinePath( LightAbstractRenderer* pRenderer, SceneObject* pInstance ){

		vec2 drawPos;
		vec2 controlPos;
		vec2 nextPos;

		uint32 number_path = m_Lines.size();
		for (uint32 i=0; i<number_path; i++){
			
			LINESTYLE2& style = m_LineStyles.at( m_Lines[i].getFillIndex() );
			
			const array<Edge>& _egdes = m_Lines[i].getEdges();
			uint32 number_edges = _egdes.size();

			drawPos = m_Lines[i].getStartPos();

			pRenderer->setPointSize( style.m_Width );
			pRenderer->setCurrentColor( style.m_Color.R01(), style.m_Color.G01(), style.m_Color.B01(), style.m_Color.A01() );

			for (uint32 j=0; j<number_edges; j++){
				const Edge& _edge = _egdes[j];

				if( _edge.getType() == Edge::eStraightEdge ){
					nextPos = drawPos + _edge.getDelta();
					pRenderer->DrawLine( drawPos, nextPos );
					drawPos = nextPos;
				}
				else {
					controlPos = drawPos + _edge.getOffLineControl();
					nextPos = controlPos + _edge.getDelta();
					pRenderer->DrawQuadBezier( drawPos, controlPos, nextPos, 10 );
					drawPos = nextPos;
				}
			}
		}
	}

	  /* ************* */
	 /*		Path	  */
	/* ************* */

	Path::Path(){
		init();
	}

	void Path::init(){
		m_pFillStyleArray = NULL;
		m_FillIndex = 0;
		m_StartPos.set(0,0);
		m_Edges.clear();
		m_triangles.clear();
		m_TriangulationSucceed = false;
		m_isClockwise = true;
		m_FillRightSide = true;
	}

	vec2 Path::getStartPos() const{
		return m_StartPos;
	}

	vec2 Path::getEndPos() const{
		vec2 endPos = m_StartPos;
		for (uint32 i=0; i<m_Edges.size(); i++){
			const Edge& edge = m_Edges[i];
			if( edge.getType() == Edge::eStraightEdge ){
				endPos.set( endPos.x + edge.getDelta().x, endPos.y + edge.getDelta().y );
			}
			else{
				endPos.set( endPos.x + edge.getOffLineControl().x, endPos.y + edge.getOffLineControl().y );
				endPos.set( endPos.x + edge.getDelta().x, endPos.y + edge.getDelta().y );
			}
		}
		return endPos;
	}

	void Path::pushFrontEdge( const Edge& edge ){

		if( edge.getType() == Edge::eStraightEdge ){
			m_StartPos.set( m_StartPos.x - edge.getDelta().x, m_StartPos.y - edge.getDelta().y );
		}
		else{
			m_StartPos.set( m_StartPos.x - edge.getDelta().x, m_StartPos.y - edge.getDelta().y );
			m_StartPos.set( m_StartPos.x - edge.getOffLineControl().x, m_StartPos.y - edge.getOffLineControl().y );
		}

		m_Edges.insert(m_Edges.begin(),edge);
	}

	void Path::pushBackEdge( const Edge& edge ){
		m_Edges.push_back( edge );
	}

	bool Path::computeIsClockwise(){
		array<vec2> borders;
		getBorderResolved(borders);

		uint32 size_borders = borders.size();
		if(size_borders >= 3){
			float SignOfArea = 0.0f;
			for (uint32 i=0; i<(size_borders-1); i++){
				const vec2& v1 = borders[i];
				const vec2& v2 = borders[i+1];

				SignOfArea += ( (v2.x - v1.x) * (v2.y + v1.y) );
			}

			if( SignOfArea >= 0 ){
				m_isClockwise = false;
			}
			else{
				m_isClockwise = true;
			}
		}

		return m_isClockwise;
	}

	void Path::getBorderResolved( array<vec2>& borders, 
									bool test_curve_inside /*= false*/, 
									bool exclude_last_if_equal_first /*= false*/,
									bool inverse_inside_curve /*= false*/ ) const {

		borders.clear();
		borders.reserve( m_Edges.size() + 1 );
		// compute border
		vec2 drawPos = getStartPos();
		vec2 offline = drawPos;
		vec2 next = drawPos;
		vec2 tempBezierAppro;

		borders.push_back( drawPos );
		uint32 number_edges = m_Edges.size();
		for (uint32 j=0; j<number_edges; j++){
			const Edge& _edge = m_Edges[j];

			if( _edge.getType() == Edge::eStraightEdge ){
				drawPos = drawPos + _edge.getDelta();
				
				if( ! borders.back().equalByEpsilon( drawPos ) ){
					borders.push_back( drawPos );
				}
			}
			else {
				offline = drawPos + _edge.getOffLineControl();
				next = offline + _edge.getDelta();
				
				if( s_ApproximateBezierCurve )
				{	
					float t = 1.0f / s_ItterationOfBezierCurveApproximation;
					for(int i = 0; i < s_ItterationOfBezierCurveApproximation-1; ++i)
					{
						tempBezierAppro.x = powf(1 - t, 2) * drawPos.x + 2.0f * (1 - t) * t * offline.x + t * t * next.x;
						tempBezierAppro.y = powf(1 - t, 2) * drawPos.y + 2.0f * (1 - t) * t * offline.y + t * t * next.y;
						t += 1.0f / s_ItterationOfBezierCurveApproximation;

						if( ! borders.back().equalByEpsilon( tempBezierAppro ) ){
							borders.push_back( tempBezierAppro );
						}
					}
					
					drawPos = next;
				}
				else
				{
					if(test_curve_inside)
					{
						vec2 DrawToNext = next - drawPos;
						vec2 DrawToOffline = offline - drawPos;
						bool offline_is_left =  DrawToNext.isLeft( DrawToOffline );
						// offline point is inside path if is on the left of a counter-clockwise vector,
						// or on the right of a clockwise vector
						bool isInside = (offline_is_left && !this->m_isClockwise) ||
										(!offline_is_left && this->m_isClockwise);
					
						isInside = inverse_inside_curve ? !isInside : isInside;

						if(isInside){
							if( ! borders.back().equalByEpsilon( offline ) ){
								borders.push_back( offline );
							}
						}
					}
					drawPos = next;
					if( ! borders.back().equalByEpsilon( drawPos ) ){
						borders.push_back( drawPos );
					}
				}
			}
		}

		if(exclude_last_if_equal_first){
			if( borders[0].equalByEpsilon( borders.back() ) ){
				borders.pop_back();
			}
		}
	}

	void Path::fillCurveTriangles(){
		if(s_ApproximateBezierCurve)
			return; // no curve triangle if we use the approximation
		
		m_ExternalCurveTris.clear();
		m_InternalCurveTris.clear();

		// compute border
		vec2 drawPos = getStartPos();
		vec2 offline = drawPos;
		vec2 next = drawPos;

		for (uint32 i=0; i<m_holes.size(); i++){
			m_holes[i]->fillCurveTriangles();
			for (uint32 j=0; j< m_holes[i]->m_ExternalCurveTris.size() ; j++){
				m_InternalCurveTris.push_back( m_holes[i]->m_ExternalCurveTris[j] );
			}	
		}

		uint32 number_edges = m_Edges.size();
		for (uint32 j=0; j<number_edges; j++){
			const Edge& _edge = m_Edges[j];

			if( _edge.getType() == Edge::eStraightEdge ){
				drawPos = drawPos + _edge.getDelta();
			}
			else {
				offline = drawPos + _edge.getOffLineControl();
				next = offline + _edge.getDelta();

				vec2 DrawToNext = next - drawPos;
				vec2 DrawToOffline = offline - drawPos;
				bool offline_is_left =  DrawToNext.isLeft( DrawToOffline );
				// offline point is inside path if is on the left of a counter-clockwise vector,
				// or on the right of a clockwise vector
				bool isInside = (offline_is_left && !this->m_isClockwise) ||
					(!offline_is_left && this->m_isClockwise);
				
				if(isInside){
					m_InternalCurveTris.push_back(drawPos);
					m_InternalCurveTris.push_back(offline);
					m_InternalCurveTris.push_back(next);
				}
				else{
					m_ExternalCurveTris.push_back(drawPos);
					m_ExternalCurveTris.push_back(offline);
					m_ExternalCurveTris.push_back(next);
				}
				drawPos = next;
			}
		}
	}

	void Path::fillUVs( const MATRIX& bitmapMatrix ){
		m_UVs.clear();
		int size_vertex = m_triangles.size();
		m_UVs.resize(size_vertex);
		for (int i=0; i<size_vertex; ++i){
			m_UVs[i] = bitmapMatrix.convertToMatrixCoord( m_triangles[i] );
		}
	}

	void Path::triangulate(){
		
		m_TriangulationSucceed = Triangulate::ProcessP2T(*this,m_triangles);
		if(m_TriangulationSucceed){
			fillCurveTriangles();
			FILLSTYLE* style = getFillStyle();
			if(style && style->isBitmapFill() ){
				Bitmap* bitmap = style->getBitmap();
				if( bitmap ){
					MATRIX scale_bitmapMatrix; scale_bitmapMatrix.setScaleMatrix( float( bitmap->getWidth() ), float( bitmap->getHeight() ) );
					scale_bitmapMatrix = style->getBitmapMatrix() * scale_bitmapMatrix;
					fillUVs( scale_bitmapMatrix );
				}
			}
			else if(style && style->isGradientFill() ){
				fillUVs( style->getGradientMatrix() );
			}
		}
	}

	bool Path::testIsHoleInside( const Path* holeToTest ) const {
		if( this->getFillIndex() != holeToTest->getFillIndex() )
			return false;

		array<vec2> MyBorders;
		this->getBorderResolved(MyBorders,true);
		vec2 MyMin, MyMax;
		Triangulate::Bounds( MyBorders, MyMin, MyMax );

		array<vec2> HoleBorders;
		holeToTest->getBorderResolved(HoleBorders,true);
		vec2 HoleMin, HoleMax;
		Triangulate::Bounds( HoleBorders, HoleMin, HoleMax );

		return (MyMin.x < HoleMin.x) && (MyMin.y < HoleMin.y) && (MyMax.x > HoleMax.x) && (MyMax.y > HoleMax.y);
	}

	void Path::addHole( Path* hole ){
		m_holes.push_back(hole);
	}

};